;(function() {
	
	// Default thumbnail path
	
	var THUMB_DEFAULT_URL = 'images/thumb_default.png';
	
	// Picasa XML namespaces
	
	var PICASA_NAMESPACE_GPHOTO = 'http://schemas.google.com/photos/2007';
	var PICASA_NAMESPACE_MEDIA = 'http://search.yahoo.com/mrss/';
	
	// Default width/height for image thumbs
	
	var ITEM_THUMB_SIDE = 48;
	
	// HTML templates for result item of each Web service
	
	var ITEM_TEMPLATES = [
		// Twitter template
		'<div itemtype="{itemtype}" itemindex="{itemindex}" class="result-item result-item-twitter ui-helper-clearfix"><div class="result-item-thumb"><img onload="Nokia.GBCServices.resultItemThumbLoaded(event)" src="{profile_image_url}"></div><div class="result-item-summary">{normalized_text}</div><div class="clearer"></div><div class="result-item-title">From: {from_user}</div><div class="result-item-date">{created_at}</div></div>',
		
		// Picasa template
		'<div itemtype="{itemtype}" itemindex="{itemindex}" class="result-item result-item-picasa ui-helper-clearfix"><div class="result-item-thumb"><img onload="Nokia.GBCServices.resultItemThumbLoaded(event)" src="{thumb_url}"></div><div class="result-item-summary">{title}</div><div class="clearer"></div><div class="result-item-title">From: {author}</div><div class="result-item-date">{published}</div></div>',
		
		// Google News template
		'<div itemtype="{itemtype}" itemindex="{itemindex}" class="result-item result-item-news ui-helper-clearfix"><div class="result-item-thumb"><img onload="Nokia.GBCServices.resultItemThumbLoaded(event)" src="{tbUrl}"></div><div class="result-item-summary">{title}</div><div class="clearer"></div><div class="result-item-title">Source: {publisher}</div><div class="result-item-date">{publishedDate}</div></div>',
		
		// Wikipedia template
		'<div itemtype="{itemtype}" itemindex="{itemindex}" class="result-item result-item-wikipedia ui-helper-clearfix"><div class="result-item-thumb"><img onload="Nokia.GBCServices.resultItemThumbLoaded(event)" src="{thumbnailImg}"></div><div class="result-item-summary">{title}</div><div class="clearer"></div><div class="result-item-title">Country: {countryCode}</div><div class="result-item-date">Distance: {distance}</div></div>'
	];
	
	// Constants idendifying the used Web services
	
	Nokia.SERVICE_TWITTER = 0;
	Nokia.SERVICE_PICASA = 1;
	Nokia.SERVICE_NEWS = 2;
	Nokia.SERVICE_WIKIPEDIA = 3;
	Nokia.SERVICE_WEATHER = 4;
	
	Nokia.SERVICE_LIST = [
		Nokia.SERVICE_TWITTER,
		Nokia.SERVICE_PICASA,
		Nokia.SERVICE_NEWS,
		Nokia.SERVICE_WIKIPEDIA,
		Nokia.SERVICE_WEATHER
	];
		
	Nokia.GBCServices = {
		
		/*
		 * Returns the kind of data that is returned from each 
		 * Web service (XML or JSON)
		 */
		getServiceFormat: function(service)
		{
			switch(service)
			{
				case Nokia.SERVICE_WEATHER:
				case Nokia.SERVICE_PICASA:
					return 'xml';
				case Nokia.SERVICE_WIKIPEDIA:
				case Nokia.SERVICE_NEWS:
				case Nokia.SERVICE_TWITTER:
				default:
					return 'json';
			}
		},
		
		/*
		 * Returns the URL to be used to call a specific Web service, 
		 * with a given location
		 */
		getServiceURL: function(service, location)
		{
			switch(service)
			{
				case Nokia.SERVICE_WEATHER:
					return 'http://api.yr.no/weatherapi/locationforecast/1.8/?lat=' + location.latitude + ';lon=' + location.longitude;
				case Nokia.SERVICE_WIKIPEDIA:
					return 'http://ws.geonames.org/findNearbyWikipediaJSON?lat=' + location.latitude + '&lng=' + location.longitude + '&maxRows=10';
				case Nokia.SERVICE_NEWS:
					return 'http://ajax.googleapis.com/ajax/services/search/news?v=1.0&rsz=large&geo=' + location.name;
				case Nokia.SERVICE_PICASA:
					var bboxDelta = 1;
					var bbox = (location.latitude - bboxDelta) + ',' + (location.longitude - bboxDelta) + ',' + 
						(location.latitude + bboxDelta) + ',' + (location.longitude + bboxDelta);
					return 'http://picasaweb.google.com/data/feed/api/all?kind=photo&max-results=10&bbox=' + bbox;
				case Nokia.SERVICE_TWITTER:
					return 'http://search.twitter.com/search.json?geocode=' + location.latitude + '%2C' + location.longitude + '%2C1000km';
				default:
					return null;
			}
		},
		
		/*
		 * Starts the retrieval of results from the various Web services
		 */
		retrieveItems: function(location, success, error)
		{
			this.serviceItems = [];
			this.currentServiceIndex = 0;
			this.location = location;
			this.successCallback = success;
			this.errorCallback = error;
			
			this.retrieveServiceItems();
		},
		
		/*
		 * Retrieves results from the next available Web service
		 */
		retrieveNextServiceItems: function()
		{
			this.currentServiceIndex++;
			
			if(this.currentServiceIndex < Nokia.SERVICE_LIST.length)
			{
				this.retrieveServiceItems();
			}
			else
			{
				this.successCallback(this.serviceItems);
			}
		},
		
		/*
		 * Retrieves results for the current Web service
		 */
		retrieveServiceItems: function()
		{
			var instance = this;

			// Gets the Web service URL
			var url = instance.getServiceURL(this.currentServiceIndex, this.location);
			
			// Checks which data format is returned from the Web service
			var dataType = instance.getServiceFormat(this.currentServiceIndex);
			
			try {
				netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
			} catch (e) {};
			
			var ajaxParams = {
				url : url,
				dataType: dataType,
				type: 'GET',
				data: {},
				success: function(data) {
					instance.serviceItemsSuccess(data)
				},
				error: function(data) {	
					instance.serviceItemsError(data)
				}
			};
			
			jQuery.ajax(ajaxParams);
		},
		
		/*
		 * Success callback for the results retrieval XMLHttpRequest object
		 */
		serviceItemsSuccess: function(data)
		{
			var serviceType = this.currentServiceIndex;
			
			// Parse the data returned from the Web service
			
			this.serviceItems[serviceType] = this.parseServiceItems(data);
			
			// Add some meta information to each result item:
			// * the array index
			// * the Web service type
			
			jQuery.each(this.serviceItems[serviceType], function(i, item) {
				item.itemindex = i;
				item.itemtype = serviceType;
			});
			
			this.retrieveNextServiceItems();
		},
		
		/*
		 * Error callback for the results retrieval XMLHttpRequest object
		 */
		serviceItemsError: function(data)
		{
			this.serviceItems[this.currentServiceIndex] = false;
			
			this.retrieveNextServiceItems();
		},
		
		/*
		 * Handle and parses the data returned from the Web services
		 */
		parseServiceItems: function(data)
		{
			var instance = this;
			
			switch(this.currentServiceIndex)
			{
				case Nokia.SERVICE_WEATHER:
				
					var items = [];
					
					// Loops the <time> DOM nodes to create the weather results array
					
					jQuery.each(data.getElementsByTagName('time'), function(i, item) {
						var itemObject = {
							'from': Nokia.GBCUtils.parseWeatherDate(item.getAttribute('from')),
							'to': Nokia.GBCUtils.parseWeatherDate(item.getAttribute('to')),
							'symbol_number': instance.getChildAttributeValue(item, 'symbol', 'number'),
							'symbol': instance.getChildAttributeValue(item, 'symbol', 'id')
						};
						
						itemObject.day = Nokia.GBCUtils.formatDate(itemObject.from);
						
						items.push(itemObject);
					});
					
					return items;
				
				case Nokia.SERVICE_WIKIPEDIA:
				
					// Loops the Wikipedia results and sets the default thumb where no thumbnail is defined
				
					jQuery.each(data.geonames, function(i, item) {
						if(!item.thumbnailImg)
						{
							item.thumbnailImg = THUMB_DEFAULT_URL;
						}
					});
				
					return data.geonames;
				
				case Nokia.SERVICE_NEWS:
				
					var items = [];
					
					// Loops the Google News results and creates the results array
					
					jQuery.each(data.responseData.results, function(i, item) {
						items.push({
							'title': item.title,
							'content': item.content,
							'unescapedUrl': item.unescapedUrl,
							'imageUrl': item.image ? item.image.url : null,
							'tbUrl': item.image ? item.image.tbUrl : THUMB_DEFAULT_URL,
							'publishedDate': Nokia.GBCUtils.formatDateTime(new Date(item.publishedDate)),
							'publisher': item.publisher
						});
					});
					
					return items;
					
				case Nokia.SERVICE_PICASA:
				
					var items = [];
					
					// Loops the Picasa results to create the results array

					jQuery.each(data.getElementsByTagName('entry'), function(i, entry) {
						
						var photoWebURL = null;
						
						var links = entry.getElementsByTagName('link');
						
						for(var i = 0; i < links.length; i++)
						{
							var linkNode = links[i];
							
							if(linkNode.getAttribute('rel') == 'alternate' && linkNode.getAttribute('type') == 'text/html')
							{
								photoWebURL = linkNode.getAttribute('href');
								
								break;
							}
						}
						
						items.push({
							'id': instance.getChildTextContent(entry, 'id'),
							'title': instance.getChildTextContent(entry, 'title'),
							'published': Nokia.GBCUtils.formatDateTime(new Date(parseInt(instance.getChildTextContent(entry, 'timestamp', PICASA_NAMESPACE_GPHOTO)))),
							'author': instance.getChildTextContent(entry.getElementsByTagName('author')[0], 'name'),
							'thumb_url': instance.getChildAttributeValue(entry, 'thumbnail', 'url', PICASA_NAMESPACE_MEDIA),
							'author_thumb_url': instance.getChildTextContent(entry, 'thumbnail', PICASA_NAMESPACE_GPHOTO),
							'url': instance.getChildAttributeValue(entry, 'content', 'src'),
							'web_url': photoWebURL
						});
					});
					
					return items;
					
				case Nokia.SERVICE_TWITTER:
				
					var instance = this;
					
					// Loops the Twitter results to apply formatting to date and text
				
					jQuery.each(data.results, function(i, item) {
						item.created_at = Nokia.GBCUtils.formatDateTime(new Date(item.created_at));
						item.normalized_text = instance.normalizeText(item.text);
					});
				
					return data.results;
			}
		},
		
		/*
		 * Splits words longer than 20 characters 
		 * to avoid layout issues due to horizontal scrolling
		 */
		normalizeText: function(txt)
		{
			var res = txt.replace(/([^ ]{10})([^ ]{10})/g, '$1 $2').replace(/([^ ]{10})([^ ]{10})/g, '$1 $2');
			
			return res;
		},
		
		/*
		 * Returns the text content of the first child 
		 * of a given DOM node and with a given name and namespace
		 */
		getChildTextContent: function(node, childName, namespace)
		{
			var child = null;
			
			if(namespace)
				child = node.getElementsByTagNameNS(namespace, childName)[0];
			else
				child = node.getElementsByTagName(childName)[0];

			var textContent = '';
			
			if(child)
			{
				jQuery.each(child.childNodes, function(i, childNode) {
					textContent += childNode.nodeValue;
				});
			}
			
			return textContent;
		},

		/*
		 * Returns a given attribute value of the first child 
		 * of a given DOM node and with a given name and namespace
		 */
		getChildAttributeValue: function(node, childName, attributeName, namespace)
		{
			var child = null;
			
			if(namespace)
				child = node.getElementsByTagNameNS(namespace, childName)[0];
			else
				child = node.getElementsByTagName(childName)[0];

			if(child)
				return child.getAttribute(attributeName);
				
			return '';
		},
		
		/*
		 * Returns the HTML snippet for a given result item
		 */
		getServiceItemSnippet: function(item)
		{
			// Use the template associated with the given service
			
			var template = ITEM_TEMPLATES[item.itemtype];
			
			// Bind the item properties with the template keys
			
			for(k in item)
			{
				template = template.replace('{' + k + '}', item[k]);
			}
			return template;
		},
		
		/*
		 * Callback used to create squared thumbnails for result items
		 */
		resultItemThumbLoaded: function(event)
		{
			var thumb = event.target;
			
			var w = thumb.offsetWidth;
			var h = thumb.offsetHeight;
			
			var prop = Math.min(w, h) / ITEM_THUMB_SIDE;
			
			w /= prop;
			h /= prop;
			
			thumb.style.width = w + 'px';
			thumb.style.height = h + 'px';
			thumb.style.top = (- (h - 78) / 2) + "px";
			thumb.style.left = (- (w - 78) / 2) + "px";
		}
		
	};
})();